package cn.edu.jxnu.seckill.access

import cn.edu.jxnu.seckill.domain.SeckillUser
import cn.edu.jxnu.seckill.redis.RedisService
import cn.edu.jxnu.seckill.redis.key.AccessKey
import cn.edu.jxnu.seckill.result.{CodeMsg, Result}
import cn.edu.jxnu.seckill.service.SeckillUserService
import com.alibaba.fastjson.JSON
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
import org.apache.commons.lang3.StringUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter

import scala.language.implicitConversions

/**
 * 访问拦截器。处理限流注解
 *
 * PS：由于if等导致的副作用可能会使没有加return的语句继续向下指向而抛出空指针
 *
 *
 * 重要提示：循环和if里面的return千万不要省略，会造成无法正常返回。一般就最后的return省略即可
 * @author 梦境迷离.
 * @time 2018年5月20日
 * @version v1.0
 */
@Service
class AccessInterceptor @Autowired() (userService: SeckillUserService, redisService: RedisService)
    extends HandlerInterceptorAdapter {

    override def preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Object): Boolean = {

        if (handler.isInstanceOf[HandlerMethod]) {
            val user = getUser(request, response)
            UserContext.setUser(user)
            val hm = handler.asInstanceOf[HandlerMethod]
            val accessLimit = hm.getMethodAnnotation(classOf[AccessLimit])
            if (accessLimit == null)
                return true
            val seconds = accessLimit.seconds()
            val maxCount = accessLimit.maxCount()
            val needLogin = accessLimit.needLogin()
            var key = request.getRequestURI()
            if (needLogin) {
                if (user == null) {
                    render(response, CodeMsg.SESSION_ERROR)
                    return false
                }
                key += "_" + user.getId()
            } else {
                //do nothing
            }
            val ak = AccessKey.withExpire(seconds)
            val count = redisService.get(ak, key, classOf[Integer])
            if (count == null) {
                redisService.set(ak, key, 1)
            } else if (count < maxCount) {
                redisService.incr(ak, key)
            } else {
                render(response, CodeMsg.ACCESS_LIMIT_REACHED)
                return false
            }
        }
        true
    }

    /**
     * 给服务端提示
     */
    private def render(response: HttpServletResponse, cm: CodeMsg) {

        response.setContentType("application/jsoncharset=UTF-8")
        val out = response.getOutputStream()
        val str = JSON.toJSONString(Result.error(cm), false)
        out.write(str.getBytes("UTF-8"))
        out.flush()
        out.close()
    }

    /**
     * 取出用户
     */
    private def getUser(request: HttpServletRequest, response: HttpServletResponse): SeckillUser = {

        var token: Any = null
        val paramToken = request.getParameter(SeckillUserService.COOKI_NAME_TOKEN)
        val cookieToken = getCookieValue(request, SeckillUserService.COOKI_NAME_TOKEN)
        if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken))
            return null
        if (StringUtils.isEmpty(paramToken))
            token = cookieToken
        else
            token = paramToken
        userService.getByToken(response, token.asInstanceOf[String])
    }

    /**
     * 从cookie取
     */
    private def getCookieValue(request: HttpServletRequest, cookiName: String): String = {

        val cookies = request.getCookies()
        if (cookies == null || cookies.length <= 0)
            return null
        for (cookie <- cookies) {
            if (cookie.getName().equals(cookiName))
                return cookie.getValue()
        }
        null
    }

}